package com.example.stack;


import com.example.entity.CalculateStack;

public class Calculator {

    public static void main(String[] args) {
        String expression = "7+2-(8-2)*5-5*3-4+20*2";
        char[] chars = expression.toCharArray();
        //创建两个栈，数栈，一个符号栈
        CalculateStack numStack = new CalculateStack(10);
        CalculateStack operStack = new CalculateStack(10);
        //定义需要的相关变量
        int res;
        StringBuilder sb = new StringBuilder(32);
        //开始while循环的扫描expression
        for (int i = 0; i < chars.length; i++) {
            //依次得到expression 的每一个字符
            char ch = chars[i];
            //判断ch是什么，然后做相应的处理
            if (operStack.isOper(ch)) {//如果是运算符
                //将之前的数字入栈
                //括号也是字符，防止出现两个连续字符导致sb为空
                if (sb.length() != 0) {
                    numStack.push(Integer.parseInt(sb.toString()));
                    //清空StringBuilder
                    sb.setLength(0);
                }
                //判断当前的符号栈是否为空
                if (!operStack.isEmpty()) {
                    //如果符号栈有操作符，就进行比较,如果当前的操作符的优先级小于等于栈中的操作符,就需要从数栈中pop出两个数,
                    //在从符号栈中pop出一个符号，进行运算，将得到结果，入数栈，然后将当前的操作符入符号栈
                    //如果上一个符号为"("，则不进行计算
                    if (operStack.peek() != '(' &&
                            operStack.priority(ch) <= operStack.priority(operStack.peek())) {
                        //计算栈顶元素
                        calculate(numStack, operStack);
                        //然后将当前的操作符入符号栈
                        operStack.push(ch);
                    } else if (ch == ')') {
                        //当遇到")"时，开始循环计算括号内的表达式，直到遇到"("
                        do {
                            calculate(numStack, operStack);
                        } while (operStack.peek() != '(');
                        //将左括号弹出
                        operStack.pop();
                    } else {
                        //如果当前的操作符的优先级大于栈中的操作符或为左括号， 就直接入符号栈.
                        // () > */ > +-
                        operStack.push(ch);
                    }
                } else {
                    //如果为空直接入符号栈..
                    operStack.push(ch);
                }
            } else { //如果是数，则直接入数栈
                //分析思路
                //1. 当处理多位数时，不能发现是一个数就立即入栈，因为他可能是多位数
                //2. 继续向后检索直到符号才入栈
                sb.append(ch);
                //如果ch已经是expression的最后一位，就直接入栈
                if (i == chars.length - 1) {
                    numStack.push(Integer.parseInt(sb.toString()));
                    //若最后一个运算符为*/，则进行计算，使得一次计算后只剩下加减运算
                    if (numStack.priority(operStack.peek()) == 1) {
                        //计算栈顶元素
                        calculate(numStack, operStack);
                    }
                }
            }
        }
        //当表达式扫描完毕，只剩下+-运算
        //因为是栈从后向前运算，运算顺序会导致结果错误如“5-3+2”，会先3+2=5，然后5-5=0。这种情况需要处理
        //处理思路： 判断下一个符号是否为减号，若是则将num1取负数，5-3+2 --> 5-(3-2)
        //另一种思路: 通过第一次计算将乘除运算全部计算出来，使第二次计算只剩下加减。然后将运算符和数字都依次转移到另一个临时栈中（等于将逆序计算转换成顺序）
        //同级顺序计算则不用考虑计算顺序问题了。但是感觉并没有减少多少代码，徒增资源。
        while (true) {
            //计算栈顶元素
            calculate(numStack, operStack);
            if (operStack.isEmpty()) {
                break;
            }
        }
        res = numStack.pop();
        System.out.println(String.format("表达式 %s = %d", expression, res));
    }

    /**
     * 计算栈顶元素
     *
     * @param numStack
     * @param operStack
     */
    public static void calculate(CalculateStack numStack, CalculateStack operStack) {
        int num1 = numStack.pop();
        int num2 = numStack.pop();
        int oper = operStack.pop();
        if (!operStack.isEmpty()) {
            if (numStack.priority(oper) == numStack.priority(operStack.peek()) &&
                    operStack.peek() == '-') {
                num1 = -num1;
            }
        }
        int res = numStack.cal(num1, num2, oper);
        System.out.println(String.format("%d %c %d = %d", num2, (char) oper, num1, res));
        //把运算的结果入数栈
        numStack.push(res);
    }

}

